﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace SharedGenomics.Core
{
    public static class ExceptionHelper
    {
        [Obsolete("Use Throw<TException>")]
        public static void ThrowIf<TException, TObject>(this TObject obj, Func<TObject, bool> predicate) where TException : Exception, new()
        {
            obj.ThrowIf<TException, TObject>(predicate, null);
        }

        [Obsolete("Use Throw<TException>")]
        public static void ThrowIf<TException, TObject>(this TObject obj, Func<TObject, bool> predicate, string message) where TException : Exception, new()
        {
            ThrowIf<TException>(predicate(obj), message);
        }

        public static void ThrowIf<TException>(bool condition) where TException : Exception, new()
        {
            ThrowIf<TException>(condition, (string)null);
        }

        public static void ThrowIf<TException>(bool condition, string message) where TException : Exception, new()
        {
            if (condition)
                throw Create<TException>(message);
        }

        public static void ThrowIf<TException>(bool condition, Func<TException> exceptionBuilderFunc) where TException : Exception
        {
            exceptionBuilderFunc.ThrowIfNull("exceptionBuilderFunc");
            if (condition) throw exceptionBuilderFunc();
        }

        public static void ThrowIfNotBetween<T>(this IComparable<T> comp, T min, T max, bool inclusive, string parameterName)
        {
            if (!comp.IsBetween(min, max, inclusive))
            {
                string message = String.Format("{0} must be in the range {1}{2},{3}{4}",
                                                parameterName ?? "Parameter",
                                                inclusive ? "[" : "(",
                                                min,
                                                max,
                                                inclusive ? "]" : ")");

                throw Create<ArgumentOutOfRangeException>(message);
            }
        }

        public static void ThrowIfLessThan<T>(this IComparable<T> comp, T min, string parameterName)
        {
            if (comp.CompareTo(min) < 0)
            {
                string message = String.Format("{0} must be greater than or equal to {1}",
                                                parameterName ?? "Parameter",
                                                min);

                throw Create<ArgumentOutOfRangeException>(message);
            }
        }

        public static void ThrowIfGreaterThan<T>(this IComparable<T> comp, T max, string parameterName)
        {
            if (comp.CompareTo(max) > 0)
            {
                string message = String.Format("{0} must be less than or equal to {1}",
                                                parameterName ?? "Parameter",
                                                max);

                throw Create<ArgumentOutOfRangeException>(message);
            }
        }

        public static void ThrowIfNull(this object o)
        {
            ThrowIfNull<ArgumentNullException>(o, (string)null);
        }

        public static void ThrowIfNull(this object o, string name)
        {
            ThrowIfNull<ArgumentNullException>(o, name);
        }

        public static void ThrowIfNull<TException>(this object o) where TException : Exception, new()
        {
            ThrowIfNull<TException>(o, (string)null);
        }

        public static void ThrowIfNull<TException>(this object o, string name) where TException : Exception, new()
        {
            if (o == null)
            {
                string message = "Object cannot be null";
                if (name != null)
                    message += String.Format(": {0}", name);

                throw Create<TException>(message);
            }
        }

        public static void ThrowIfNull<TException>(this object o, Func<TException> exceptionBuilderFunc) where TException : Exception
        {
            exceptionBuilderFunc.ThrowIfNull("exceptionBuilderFunc");
            if (o == null) throw exceptionBuilderFunc();
        }

        private static TException Create<TException>(string message) where TException : Exception, new()
        {
            if (message == null)
                return new TException();

            //if a message is provided, try to find a constructor with a single string argument
            ConstructorInfo ci = typeof(TException).GetConstructor(new Type[] { typeof(string) });
            return ci == null
                ? new TException()
                : (TException)ci.Invoke(new object[] { message });
        }
    }
}
